﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IQToolkit.Data.Common
{
    using System.Linq.Expressions;
    using System.Collections.ObjectModel;
    using IQToolkit;

    public class EntityTableExpression : DbExpression
    {
        Type entity;
        Expression expression;

        public EntityTableExpression(Type entity, Expression expression)
            : base(DbExpressionType.EntityTable, expression.Type)
        {
            this.entity = entity;
            this.expression = expression;
        }

        public Type Entity
        {
            get { return this.entity; }
        }

        public Expression Expression
        {
            get { return this.expression; }
        }
    }

    public class EntityColumnDeclaration: ColumnDeclaration
    {
        public EntityColumnDeclaration(string name, Expression expression, QueryType queryType)
            : base(name, expression, queryType)
        {
        }
    }

    public class SioMdmSqlFormatter : SqlFormatter
    {
        protected Dictionary<string, Object> _parameters;
        protected Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> _aliases;
        protected List<Sio.Mdm.Linq.TableAlias> _fieldsAliases;
        private bool _whereFormatter;
        private bool _fieldsFormatter;
        private IQToolkit.Data.DbTypeSystem dbTypeSystem = new IQToolkit.Data.DbTypeSystem();

        protected bool WhereFormatter
        {
            get { return _whereFormatter; }
            set
            {
                _whereFormatter = value; 
                if (value && (_parameters == null)) _parameters = new Dictionary<String, Object>(); 
            } 
        }

        protected SioMdmSqlFormatter(QueryLanguage language)
            : base(language)
        {
        }

        protected string BuildFormatFields(Expression expression, ref Dictionary<string, Object> parameters, List<Sio.Mdm.Linq.TableAlias> aliases)
        {
            this._fieldsFormatter = true;
            this._fieldsAliases = aliases;
            _parameters = parameters;
            if (!(expression is ConstantExpression))  //if expression is just constant then I skip it
            {
                this.Visit(expression);
            }

            parameters = this._parameters;
            return this.ToString();
        }

        protected string FormatWhere(Expression expression, out Dictionary<string, Object> parameters, out Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> aliases)
        {
            this.WhereFormatter = true;

            if (!(expression is ConstantExpression))  //if expression is just constant then I skip it
            {
                _aliases = Sio.Mdm.Linq.TypeVisitor.Find(expression);
                var exp = expression;
                this.Visit(exp);
            }

            parameters = this._parameters;
            aliases = this._aliases;
            return this.ToString();
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (WhereFormatter || _fieldsFormatter)
            {
                //parametrize
                string par = String.Format("{0}", _parameters.Count + 1);
                if (_parameters == null) _parameters = new Dictionary<string, object>();
                _parameters.Add(par, c.Value);
                var result = new NamedValueExpression(par, dbTypeSystem.GetColumnType(c.Type), c);
                return base.VisitNamedValue(result);
            }
            else 
                
                return base.VisitConstant(c);
        }

        protected override Expression VisitMemberAccess(MemberExpression m)
        {
            if (_fieldsFormatter)
            {
                Type entityType = m.Expression.Type;
                string name;
                if (m.Expression.NodeType == ExpressionType.MemberAccess)
                    name = ((MemberExpression)m.Expression).Member.Name;
                else
                    name = ((ParameterExpression)m.Expression).Name;
    
                var info = Sio.Mdm.Entities.EntityFactory.GetEntityInfo(entityType);

                //find alias
                string aliasName = _fieldsAliases.Where(r => (r.OriginalName.Equals(name)) && r.EntityType.Name.Equals(entityType.Name)).Select(r => r.LocalName).FirstOrDefault();
                if (aliasName == null)
                    aliasName = _fieldsAliases.Where(r => r.EntityType.Name.Equals(entityType.Name)).Select(r => r.LocalName).FirstOrDefault();

                if (aliasName == null)
                    throw new InvalidOperationException(String.Format("Unknown alias {0} for entity {1}", name, entityType));

                this.WriteAliasName(String.Format("{0}", aliasName));


                this.Write(".");
                if (info.Fields.ContainsKey(m.Member.Name.ToLower()))
                {
                    this.WriteColumnName(info.Fields[m.Member.Name.ToLower()].Name);
                }
                else
                    throw new NotSupportedException(String.Format("Invalid column {0}", m.Member.Name));

                return m;
            }
            else

            if (WhereFormatter)
            {
                Type entityType = ((ParameterExpression)m.Expression).Type;
                var info = Sio.Mdm.Entities.EntityFactory.GetEntityInfo(entityType);
                this.WriteAliasName(String.Format("{0}", ((ParameterExpression)m.Expression).Name));
                this.Write(".");
                if (info.Fields.ContainsKey(m.Member.Name.ToLower()))
                {
                    this.WriteColumnName(info.Fields[m.Member.Name.ToLower()].Name);
                }
                else
                    throw new NotSupportedException(String.Format("Invalid column {0}", m.Member.Name));

                return m;
            }
            else return base.VisitMemberAccess(m);
        }

        /// <summary>
        /// Alias has to be quoted
        /// </summary>
        /// <param name="columnName"></param>
        protected override void WriteAsColumnName(string columnName)
        {
            this.Write("AS ");
            string name = (this.Language != null) ? this.Language.Quote(columnName) : columnName;
            this.Write(name);
        }

        protected override void WriteTableName(string tableName)
        {
            //string name = (this.Language != null) ? this.Language.Quote(tableName) : tableName;
            this.Write(String.Format("{0}", tableName.ToUpper()));
        }

        /// <summary>
        /// Column names shuldn't be quoted
        /// </summary>
        /// <param name="columnName"></param>
        protected override void WriteColumnName(string columnName)
        {
            this.Write(columnName.ToUpper());
        }

        protected override Expression VisitColumn(ColumnExpression column)
        {
            if (column.Alias != null && !this.HideColumnAliases)
            {
                this.WriteAliasName(GetAliasName(column.Alias));
                this.Write(".");
            }
            this.WriteColumnName(column.Name);
            return column;
        }
        
    }       

}
